package com.sc.nft.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.lang.WeightRandom;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.sc.nft.dao.*;
import com.sc.nft.dto.ExpendCollectionDTO;
import com.sc.nft.entity.*;
import com.sc.nft.entity.dto.SaasTaskBurialPointDTO;
import com.sc.nft.entity.dto.SalvageEventsRewardsDTO;
import com.sc.nft.entity.vo.*;
import com.sc.nft.entity.vo.salvageEvents.SalvageEventsPageVO;
import com.sc.nft.entity.vo.salvageEvents.SalvageEventsScreenVO;
import com.sc.nft.enums.*;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.helper.MessageQueueHelper;
import com.sc.nft.service.*;
import com.sc.nft.sup.ErrorCode;
import com.sc.nft.util.IdCardAuditUtil;
import com.sc.nft.util.OrderNoUtil;
import jodd.typeconverter.Convert;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 分解活动藏品奖励表服务接口实现
 *
 * @author Sun
 * @description 由 Mybatisplus Code Generator 创建
 * @since 2023-08-10 13:40:55
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class SalvageEventsDetailsServiceImpl implements SalvageEventsDetailsService {

    private final SalvageEventsInfoDao salvageEventsInfoDao;
    private final SalvageEventsDetailsDao salvageEventsDetailsDao;
    private final UserCollectionDao userCollectionDao;
    private final DigitalCollectionDao digitalCollectionDao;
    private final UserCollectionService userCollectionService;
    private final UserSalvageEventsRecordsDao userSalvageEventsRecordsDao;
    private final UserAssetsService userAssetsService;
    private final UserEquityPropsService userEquityPropsService;
    private final EquityPropsDao equityPropsDao;
    private final UserWalletDao userWalletDao;
    private final UserWalletDetailDao userWalletDetailDao;
    private final ExpenditureRecordDao expenditureRecordDao;
    private final UserConsensusService userConsensusService;
    private final MessageQueueHelper messageQueueHelper;
    private final UserEcologyPointsService userEcologyPointsService;
    private final UserEcologyCastUpperLimitService userEcologyCastUpperLimitService;
    private final UserEcologyReforgeUpperLimitService userEcologyReforgeUpperLimitService;
    private final UserSalvageEventsCardRecordsDao userSalvageEventsCardRecordsDao;
    private final FilterIncomeListDao filterIncomeListDao;
    private final ExitConsensusUserDao exitConsensusUserDao;

    @Override
    public Page<SalvageEventsPageVO> getSalvageEventsPage(NFTProductShowSalvagePrefectureEnum showSalvagePrefecture, Integer pageNo, Integer pageSize) {
        return salvageEventsInfoDao.getSalvageEventsPage(showSalvagePrefecture, pageNo, pageSize);
    }

    @Override
    public Page<UserCollectionBySalvageEventsIdVO> getUserCollectionBySalvageEventsId(Long id, UserInfo user, List<Long> collectionIds, Integer pageNo, Integer pageSize) {
        SalvageEventsInfo salvageEvents = salvageEventsInfoDao.getById(id);
        Assert.notNull(salvageEvents, () -> new GlobalRunTimeException("分解活动不存在"));

        Assert.isTrue(salvageEvents.getIsListing(), () -> new GlobalRunTimeException("分解活动未上架"));

        DateTime now = DateTime.now();
        if (now.isBefore(salvageEvents.getBeginTime())) {
            throw new GlobalRunTimeException("分解未到开启时间");
        }

        if (now.after(salvageEvents.getEndTime())) {
            throw new GlobalRunTimeException("分解已经结束");
        }

        if (collectionIds.size() == 0) {
            //获取分解活动的关联藏品id
            List<Long> colectionIds = salvageEventsDetailsDao.getCollectionIdBySalvageEventsId(salvageEvents.getId());
            Assert.isTrue(colectionIds.size() > 0, () -> new GlobalRunTimeException("分解活动没有关联藏品"));

            return userCollectionDao.getUserCollectionBySalvageEventsId(user.getId(), colectionIds, pageNo, pageSize);
        } else {
            //获取分解活动的关联藏品id
            List<Long> colectionIds = salvageEventsDetailsDao.getCollectionIdBySalvageEventsIdAndCollectionIds(salvageEvents.getId(), collectionIds);
            Assert.isTrue(colectionIds.size() > 0, () -> new GlobalRunTimeException("暂未持有全中的藏品"));
            return userCollectionDao.getUserCollectionBySalvageEventsId(user.getId(), colectionIds, pageNo, pageSize);
        }

    }

    @Override
    public List<GetSalvageEventsRewardsVO> getSalvageEventsRewards(Long id, UserInfo user, List<Long> userCollectionIds) {
        SalvageEventsInfo salvageEvents = salvageEventsInfoDao.getById(id);
        Assert.notNull(salvageEvents, () -> new GlobalRunTimeException("分解活动不存在"));
        Assert.isTrue(salvageEvents.getIsListing(), () -> new GlobalRunTimeException("分解活动未上架"));
        if (userCollectionIds.size() > 0) {
            Assert.isFalse(userCollectionIds.size() > 20, () -> new GlobalRunTimeException("一次最多分解20个藏品"));
        }

        DateTime now = DateTime.now();
        if (now.isBefore(salvageEvents.getBeginTime())) {
            throw new GlobalRunTimeException("分解未到开启时间");
        }

        if (now.after(salvageEvents.getEndTime())) {
            throw new GlobalRunTimeException("分解已经结束");
        }

        //校验传入进来的藏品 是否符号分解活动的要求
        //1.持有 2.上链 3.未质押/激活 4.未寄售 5.已开启盲盒 6.未删除
        List<UserCollection> userCollectionList = userCollectionDao.getCollectionIdByIds(user.getId(), userCollectionIds);
        Assert.isTrue(userCollectionList.size() == userCollectionIds.size(), () -> new GlobalRunTimeException("选择的藏品中,其实包含不满足分解条件!"));
        Set<Long> collectionIds = userCollectionList.stream().map(UserCollection::getCollectionId).collect(Collectors.toSet());
        Assert.isTrue(collectionIds.size() > 0, () -> new GlobalRunTimeException("选择的藏品中,其实包含不满足分解条件!"));
        return decompositionRewardResultsAggregation(getSalvageEventsRewardsByUserCollectionListAndSalvageEventsInfo(userCollectionList, salvageEvents));
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<GetSalvageEventsRewardsVO> salvageUserCollection(Long id, UserInfo user, List<Long> userCollectionIds, List<SalvageCardVO> cardInfo) throws IOException {
        SalvageEventsInfo salvageEvents = salvageEventsInfoDao.getById(id);
        Assert.notNull(salvageEvents, () -> new GlobalRunTimeException("分解活动不存在"));
        Assert.isTrue(salvageEvents.getIsListing(), () -> new GlobalRunTimeException("分解活动未上架"));

        DateTime now = DateTime.now();
        if (now.isBefore(salvageEvents.getBeginTime())) {
            throw new GlobalRunTimeException("分解未到开启时间");
        }

        if (now.after(salvageEvents.getEndTime())) {
            throw new GlobalRunTimeException("分解已经结束");
        }

        if (userCollectionIds.size() > 0) {
            Assert.isFalse(userCollectionIds.size() > salvageEvents.getSingleCount(), () -> new GlobalRunTimeException(StrUtil.format("单次分解不能超过:{}", salvageEvents.getSingleCount())));
        }

        Assert.isTrue(salvageEvents.getUsableCount() > 0, () -> new GlobalRunTimeException("活动已达上限"));
        Assert.isTrue(salvageEvents.getUsableCount() - userCollectionIds.size() >= 0, () -> new GlobalRunTimeException("活动已达上限"));
        //不是玛特的市值账号需要判断数量
        if (!filterIncomeListDao.checkListIsFilterAccount(Arrays.asList(6L, 7L, 131L, 145L), user.getId())) {
            Integer recordsCount = userSalvageEventsRecordsDao.getCountByUserIdAndEventsId(user.getId(), id);
            Assert.isTrue(salvageEvents.getUserCount() - (recordsCount + userCollectionIds.size()) >= 0, () -> new GlobalRunTimeException("个人参与已达上限"));
        }
        //校验传入进来的藏品 是否符号分解活动的要求
        //1.持有 2.上链 3.未质押/激活 4.未寄售 5.已开启盲盒 6.未删除 7.不为仅熔炼
        List<UserCollection> userCollectionList = userCollectionDao.getCollectionIdByIds(user.getId(), userCollectionIds);
        Assert.isTrue(CollectionUtil.isNotEmpty(userCollectionList) && userCollectionList.size() == userCollectionIds.size(), () -> new GlobalRunTimeException("选择的藏品中,其实包含不满足分解条件!"));
        Map<Long, Integer> collectionCountMap = userCollectionList.stream().collect(Collectors.groupingBy(UserCollection::getCollectionId, Collectors.summingInt(i -> 1)));
        List<Long> collectionIdList = new ArrayList<>(collectionCountMap.keySet());
        List<SalvageEventsDetails> eventsDetailsList = salvageEventsDetailsDao.getListByCollectionIds(salvageEvents.getId(), collectionIdList);
        Assert.isTrue(CollectionUtil.isNotEmpty(eventsDetailsList), () -> new GlobalRunTimeException("选择的藏品中,其实包含不满足分解条件!"));
        Map<Long, List<SalvageEventsDetails>> salvageEventsDetailsMap = eventsDetailsList.stream().collect(Collectors.groupingBy(SalvageEventsDetails::getCollectionId));
        collectionIdList.forEach(collectionId -> {
            Assert.isTrue(CollectionUtil.isNotEmpty(salvageEventsDetailsMap.get(collectionId)), () -> new GlobalRunTimeException("选择的藏品中,其实包含不满足分解条件!"));
        });
        List<DigitalCollection> digitalCollectionList = digitalCollectionDao.getByIds(collectionIdList);
        Assert.isTrue(CollectionUtil.isNotEmpty(digitalCollectionList) && digitalCollectionList.size() == collectionIdList.size(), () -> new GlobalRunTimeException("藏品不存在"));
        Map<Long, DigitalCollection> digitalCollectionMap = digitalCollectionList.stream().collect(Collectors.toMap(DigitalCollection::getId, Function.identity()));
        //是否退出共识区
        ExitConsensusUser exitConsensusUser = exitConsensusUserDao.getByUserId(user.getId());

        if (salvageEvents.getShowSalvagePrefecture() == NFTProductShowSalvagePrefectureEnum.TICKET_EXCHANGE) {

            Assert.isTrue(cardInfo.size() > 0, () -> new GlobalRunTimeException("用户实名未填写"));
            HashSet<String> collect = cardInfo.stream().map(SalvageCardVO::getCardNo).collect(Collectors.toCollection(HashSet::new));
            Assert.isTrue(collect.size() == cardInfo.size(), () -> new GlobalRunTimeException("实名信息存在重复"));

            for (SalvageCardVO salvageCardVO : cardInfo) {
                Assert.isTrue(Validator.isMobile(salvageCardVO.getMobilePhone()), () -> new GlobalRunTimeException(StrUtil.format("{} 手机号不合法", salvageCardVO.getMobilePhone())));
                Assert.isTrue(Validator.isCitizenId(salvageCardVO.getCardNo()), () -> new GlobalRunTimeException(StrUtil.format("{} 身份证不合法", salvageCardVO.getCardNo())));
                Assert.isTrue(Validator.isChinese(salvageCardVO.getRealName()), () -> new GlobalRunTimeException(StrUtil.format("{} 姓名不合法", salvageCardVO.getRealName())));

                Assert.isFalse(
                        userSalvageEventsCardRecordsDao.getCountByCardNo(salvageCardVO.getCardNo()) >= 1,
                        () -> new GlobalRunTimeException("每个身份证只能兑换一次"));

                Map<String, String> param = new HashMap<>();
                param.put("cardNo", salvageCardVO.getCardNo());
                param.put("realName", salvageCardVO.getRealName());
                JSONObject jsonObject = IdCardAuditUtil.twoElementsOfIdCard(param);
                Assert.notNull(jsonObject, () -> new GlobalRunTimeException("系统繁忙,请稍后重试"));
                String code = jsonObject.getString("error_code");
                Assert.isTrue("0".equals(code), () -> new GlobalRunTimeException("实名异常,请稍后重试"));

                Boolean isok = jsonObject.getJSONObject("result").getBoolean("isok");
                Assert.isTrue(isok, () -> new GlobalRunTimeException(StrUtil.format("{} 身份证号与姓名不符", salvageCardVO.getRealName())));
            }
        }

        List<SalvageEventsRewardsDTO> dtoList = Lists.newArrayList();
        //把持有的藏品和数量统计出来 {"藏品id":x,"数量":x}
        Integer sumCount = userCollectionList.size();
        String ids = "";
        Map<Long, Integer> consumeConsensusMap = Maps.newHashMap();

        List<ExpendCollectionDTO> expendCollection = Lists.newArrayList();
        for (Map.Entry<Long, Integer> entry : collectionCountMap.entrySet()) {
            Long collectionId = entry.getKey();
            int count = entry.getValue();
            log.info("藏品id:{},选择数量:{}", collectionId, count);
            //根据藏品id,获取分解活动的藏品奖励
            List<SalvageEventsDetails> salvageEventsDetailsList = salvageEventsDetailsMap.get(collectionId);
            if (Objects.equals(salvageEventsDetailsList.get(0).getSalvageMode(), SalvageEventsModeEnum.FIXED)) {
                //region 固定模式
                for (SalvageEventsDetails salvageEventsDetails : salvageEventsDetailsList) {
                    SalvageEventsRewardsDTO salvageEventsRewardsDTO = new SalvageEventsRewardsDTO();
                    salvageEventsRewardsDTO.setLinkCount(salvageEventsDetails.getLinkCount().multiply(Convert.toBigDecimal(count)));
                    salvageEventsRewardsDTO.setLinkType(salvageEventsDetails.getLinkType());
                    salvageEventsRewardsDTO.setLinkName(salvageEventsDetails.getLinkName());
                    salvageEventsRewardsDTO.setLinkId(salvageEventsDetails.getLinkId());
                    dtoList.add(salvageEventsRewardsDTO);
                }
                ids = salvageEventsDetailsList.stream().map(SalvageEventsDetails::getId).map(String::valueOf).collect(Collectors.joining(","));
                //endregion
            } else {
                //region 比例模式
                int usableCount = count;
                Set<Long> detailIdSet = Sets.newHashSet();
                while (usableCount > 0) {
                    List<WeightRandom.WeightObj<Long>> weightObjList = new ArrayList<WeightRandom.WeightObj<Long>>();
                    salvageEventsDetailsList.stream().filter(v -> v.getSalvageUsableNum() > 0).forEach(v -> weightObjList.add(new WeightRandom.WeightObj<>(v.getId(), v.getSalvageProportion().movePointLeft(2).doubleValue())));
                    Assert.isTrue(CollectionUtil.isNotEmpty(weightObjList), () -> new GlobalRunTimeException("活动已达上限"));
                    Long detailId = RandomUtil.weightRandom(weightObjList).next();
                    detailIdSet.add(detailId);
                    salvageEventsDetailsList.stream().filter(v -> Objects.equals(v.getId(), detailId)).forEach(v -> {
                        v.setSalvageUsableNum(v.getSalvageUsableNum() - 1);
                        SalvageEventsRewardsDTO salvageEventsRewardsDTO = new SalvageEventsRewardsDTO();
                        salvageEventsRewardsDTO.setLinkCount(v.getLinkCount());
                        salvageEventsRewardsDTO.setLinkType(v.getLinkType());
                        salvageEventsRewardsDTO.setLinkName(v.getLinkName());
                        salvageEventsRewardsDTO.setLinkId(v.getLinkId());
                        dtoList.add(salvageEventsRewardsDTO);
                    });
                    usableCount--;
                }
                salvageEventsDetailsList.stream().filter(v -> detailIdSet.contains(v.getId())).forEach(v -> {
                    Assert.isTrue(salvageEventsDetailsDao.updateSalvageUsableNum(v.getSalvageUsableNum(), v.getId()), () -> new GlobalRunTimeException("分解失败"));
                });
                ids = detailIdSet.stream().map(String::valueOf).collect(Collectors.joining(","));
                //endregion
            }
            DigitalCollection collection = digitalCollectionMap.get(collectionId);
            ExpendCollectionDTO expendCollectionDTO = new ExpendCollectionDTO();
            expendCollectionDTO.setCollectionImage(collection.getCreationContent());
            expendCollectionDTO.setFirstTitle(collection.getFirstTitle());
            expendCollectionDTO.setCount(count);
            expendCollection.add(expendCollectionDTO);

            //region 共识度
            if (salvageEvents.getConsumeConsensus()
                    && collection.getCollectionType() == DigitalMarketTypeEnum.ECOLOGY
                    && (BeanUtil.isEmpty(exitConsensusUser) || !exitConsensusUser.getStatus())) {
                consumeConsensusMap.put(collectionId, count);
            }
            //endregion
        }

        List<GetSalvageEventsRewardsVO> voList = decompositionRewardResultsAggregation(dtoList);

        //记录用户分解活动记录
        UserSalvageEventsRecords userSalvageEventsRecords = new UserSalvageEventsRecords();
        userSalvageEventsRecords.setEventsId(salvageEvents.getId());
        userSalvageEventsRecords.setEventsDetailsIds(ids.substring(0, ids.length() - 1));
        userSalvageEventsRecords.setUserId(user.getId());
        userSalvageEventsRecords.setEventsName(salvageEvents.getEventsName());
        userSalvageEventsRecords.setCount(sumCount);
        userSalvageEventsRecords.setExpendCollection(JSONUtil.toJsonStr(expendCollection));
        userSalvageEventsRecords.setRewardsDetails(JSONUtil.toJsonStr(voList));
        userSalvageEventsRecords.setOrderNo(OrderNoUtil.getOrderNo());
        userSalvageEventsRecords.insert();

        if (salvageEvents.getShowSalvagePrefecture() == NFTProductShowSalvagePrefectureEnum.TICKET_EXCHANGE) {
            for (SalvageCardVO salvageCardVO : cardInfo) {
                UserSalvageEventsCardRecords userSalvageEventsCardRecords = new UserSalvageEventsCardRecords();
                userSalvageEventsCardRecords.setUserRecordsId(userSalvageEventsRecords.getId());
                userSalvageEventsCardRecords.setOrderNo(userSalvageEventsRecords.getOrderNo());
                userSalvageEventsCardRecords.setRealName(salvageCardVO.getRealName());
                userSalvageEventsCardRecords.setCardNo(salvageCardVO.getCardNo());
                userSalvageEventsCardRecords.setMobilePhone(salvageCardVO.getMobilePhone());
                userSalvageEventsCardRecords.setRemark(StrUtil.EMPTY);
                userSalvageEventsCardRecords.insert();
            }
        }

        Assert.isTrue(salvageEventsInfoDao.subUsableCountById(salvageEvents.getId(), sumCount), () -> new GlobalRunTimeException("系统繁忙,请稍后再试"));

        //region 共识度
        consumeConsensusMap.forEach((key, value) -> {
            userConsensusService.adjustConsensusService(user.getId(), key, (new BigDecimal(value)).negate(), UserConsensusTypeEnums.DECOMPOSE, digitalCollectionMap.get(key).getFirstTitle(), null);
        });
        //endregion

        // 给用户发放奖励
        for (GetSalvageEventsRewardsVO getSalvageEventsRewardsVO : voList) {
            switch (getSalvageEventsRewardsVO.getLinkType()) {
                case INTEGRAL:
                    userAssetsService.addIntegralByUserId(NumberUtil.toBigDecimal(getSalvageEventsRewardsVO.getLinkCount()), user.getId(), CoinCategoryEnum.INTEGRAL, AssetChangeLogEnum.DECOMPOSE, userSalvageEventsRecords.getId(), userSalvageEventsRecords.getEventsName());
                    break;
                case PROPS:
                    EquityProps equityProps = equityPropsDao.getById(getSalvageEventsRewardsVO.getLinkId());
                    Assert.notNull(equityProps, () -> new GlobalRunTimeException(ErrorCode.EQUITY_PROPS_DO_NOT_EXIST));
                    userEquityPropsService.addEquityPropsByUser(equityProps, user.getId(), UserEquityPropsGetTypeEnum.DECOMPOSE, NumberUtil.parseInt(getSalvageEventsRewardsVO.getLinkCount()), userSalvageEventsRecords.getEventsName(), userSalvageEventsRecords.getId()
                            , BigDecimal.ZERO, UserEquityPropsGetTypeEnum.DECOMPOSE, userSalvageEventsRecords.getId());
                    break;
                case ROUGH_STONE:
                    UserWallet userWallet = userWalletDao.getByUserId(user.getId());
                    UserWalletDetail userWalletDetail = new UserWalletDetail();
                    userWalletDetail.setUserId(user.getId());
                    userWalletDetail.setType(WalletDetailTypeEnum.DECOMPOSE);
                    userWalletDetail.setAmount(NumberUtil.toBigDecimal(getSalvageEventsRewardsVO.getLinkCount()));
                    userWalletDetail.setCharge(BigDecimal.ZERO);
                    userWalletDetail.setBalanceType(UserBalanceTypeEnum.PAY_BALANCE);
                    userWalletDetail.setRealAmount(userWalletDetail.getAmount());
                    userWalletDetail.setAfterBalance(userWallet.getPayBalance().add(userWalletDetail.getAmount()));
                    userWalletDetail.setBeforeBalance(userWallet.getPayBalance());
                    userWalletDetail.setIsAdd(Boolean.TRUE);
                    userWalletDetailDao.insert(userWalletDetail);

                    ExpenditureRecord expenditureRecord = new ExpenditureRecord();
                    expenditureRecord.setUserId(user.getId());
                    expenditureRecord.setAmount(userWalletDetail.getAmount());
                    expenditureRecord.setCharge(BigDecimal.ZERO);
                    expenditureRecord.setRealAmount(userWalletDetail.getAmount());
                    expenditureRecord.setType(ExpenditureRecordTypeEnum.RED_PACKET);
                    expenditureRecord.setBeforeBalance(userWalletDetail.getBeforeBalance());
                    expenditureRecord.setAfterBalance(userWalletDetail.getAfterBalance());
                    expenditureRecord.setRemark(userSalvageEventsRecords.getEventsName());
                    expenditureRecordDao.insert(expenditureRecord);
                    //加钱
                    userWalletDao.addPayBalance(user.getId(), userWalletDetail.getAmount());
                    break;
                case FUEL_RELEASE_ALLOWANCE:
                    //维护用户生态积分上限 并且新增记录
                    userEcologyPointsService.addUsableAmountUpperLimitByUserId(user.getId(), NumberUtil.toBigDecimal(getSalvageEventsRewardsVO.getLinkCount()), UserEcologyUsableAmountUpperLimitDetailTypeEnum.DECOMPOSITION_ACTIVITY, salvageEvents.getEventsName());
//                    Assert.isTrue(userEcologyPointsDao.addUsableAmountUpperLimitByUserId(user.getId(), NumberUtil.toBigDecimal(getSalvageEventsRewardsVO.getLinkCount())), () -> new GlobalRunTimeException("系统繁忙,请稍后重试"));
                    //冻结燃料释放
                    messageQueueHelper.sendMessage(MessageTopic.ECOLOGY_HATCH_TOPIC, user.getId());
                    break;
                case UPPER_CASTING_LIMIT:
                    //铸造上限
                    userEcologyCastUpperLimitService.addCountByUserIdAndCollectionId(
                            user.getId(), getSalvageEventsRewardsVO.getLinkId(), Convert.toInteger(getSalvageEventsRewardsVO.getLinkCount())
                            , UserEcologyCastUpperLimitDetailsTypeEnum.SALVAGE, salvageEvents.getEventsName(), userSalvageEventsRecords.getId());
                    break;
                case UPPER_REFORGE_CASTING_LIMIT:
                    //重铸上限
                    userEcologyReforgeUpperLimitService.addCountByUserIdAndCollectionId(
                            user.getId(), getSalvageEventsRewardsVO.getLinkId(), Convert.toInteger(getSalvageEventsRewardsVO.getLinkCount())
                            , UserEcologyReforgeUpperLimitDetailsTypeEnum.SALVAGE, salvageEvents.getEventsName(), userSalvageEventsRecords.getId());
                    break;
                default:
                    throw new GlobalRunTimeException("分解活动奖励类型错误");
            }
        }

        //处理藏品(转增到平台账户)
        userCollectionService.recoverCollections(userCollectionList);

        // 全都完事以后发送埋点数据
        for (UserCollection userCollection : userCollectionList) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() {
                    SaasTaskBurialPointDTO saasTaskBurialPointDTO = new SaasTaskBurialPointDTO();
                    messageQueueHelper.sendMessage(MessageTopic.DISK_TAKER, saasTaskBurialPointDTO.wallet(user.getId(), SaasTaskTypeEnum.DISASSEMBLE_THE_COLLECTION, BigDecimal.ONE, 1, userCollection.getCollectionId()), SaasTaskTypeEnum.DISASSEMBLE_THE_COLLECTION.name());
                }
            });
        }
        return voList;
    }

    @Override
    public Page<UserSalvageEventsRecordsVO> userSalvageEventsRecords(UserInfo user, Integer pageNo, Integer pageSize) {
        return userSalvageEventsRecordsDao.userSalvageEventsRecords(user.getId(), pageNo, pageSize);
    }

    @Override
    public List<SalvageEventsScreenVO> getSalvageEventsScreen(Long id) {
        return salvageEventsDetailsDao.getSalvageEventsScreen(id);
    }

    @Override
    public SalvageUserCountVO getSalvageUserCount(UserInfo user, Long id) {
        SalvageEventsInfo salvageEvents = salvageEventsInfoDao.getById(id);
        Assert.notNull(salvageEvents, () -> new GlobalRunTimeException("分解活动不存在"));
        Assert.isTrue(salvageEvents.getIsListing(), () -> new GlobalRunTimeException("分解活动未上架"));

        DateTime now = DateTime.now();
        if (now.isBefore(salvageEvents.getBeginTime())) {
            throw new GlobalRunTimeException("分解未到开启时间");
        }

        if (now.after(salvageEvents.getEndTime())) {
            throw new GlobalRunTimeException("分解已经结束");
        }
        Integer recordsCount = userSalvageEventsRecordsDao.getCountByUserIdAndEventsId(user.getId(), id);
        SalvageUserCountVO salvageUserCountVO = new SalvageUserCountVO();
        int count = salvageEvents.getUserCount() - recordsCount;
        salvageUserCountVO.setUserCount(count < 0 ? 0 : count);
        salvageUserCountVO.setUsableCount(salvageEvents.getUsableCount() < 0 ? 0 : salvageEvents.getUsableCount() * salvageEvents.getSumCountCoefficient());
        salvageUserCountVO.setUserCountConf(salvageEvents.getUserCount());
        salvageUserCountVO.setSumCountConf(salvageEvents.getSumCount() * salvageEvents.getSumCountCoefficient());
        salvageUserCountVO.setUsableCountConf(salvageEvents.getUsableCount() * salvageEvents.getSumCountCoefficient());
        //是玛特的市值账号
        if (filterIncomeListDao.checkListIsFilterAccount(Arrays.asList(6L, 7L, 131L, 145L), user.getId())) {
            salvageUserCountVO.setUserCount(salvageEvents.getUsableCount());
            salvageUserCountVO.setUserCountConf(salvageEvents.getUsableCount());
        }
        return salvageUserCountVO;
    }

    @Override
    public TicketDetailsVO getTicketDetails(UserInfo user) {
        //获取门票活动
        SalvageEventsInfo salvageEventsInfo = salvageEventsInfoDao.getLastOneByShowSalvagePrefecture(NFTProductShowSalvagePrefectureEnum.TICKET_EXCHANGE);
        Assert.notNull(salvageEventsInfo, () -> new GlobalRunTimeException("暂未查询到活动,请稍后重试"));
        //获取分解活动详情
        List<UserHoldTicketDetailsVO> voList = salvageEventsDetailsDao.getCInfoBySalvageEventsId(salvageEventsInfo.getId());
        Assert.isTrue(voList.size() > 0, () -> new GlobalRunTimeException("活动暂未配置,请稍后重试"));


        Assert.isTrue(salvageEventsInfo.getIsListing(), () -> new GlobalRunTimeException("分解活动未上架"));
        for (UserHoldTicketDetailsVO userHoldTicketDetailsVO : voList) {
            userHoldTicketDetailsVO.setUserCollectionIds(userCollectionDao.getHoldIdByCollectionNotConsignId(userHoldTicketDetailsVO.getCollectionId(), user.getId()));
        }

        TicketDetailsVO ticketDetailsVO = BeanUtil.copyProperties(salvageEventsInfo, TicketDetailsVO.class);
        //获取用户持有的藏品信息
        ticketDetailsVO.setHoldColectionDetails(voList);

        ticketDetailsVO.setBeginTimeStamp(ticketDetailsVO.getBeginTime().getTime());
        ticketDetailsVO.setEndTimeStamp(ticketDetailsVO.getEndTime().getTime());
        ticketDetailsVO.setNowTimeStamp(DateTime.now().getTime());
        return ticketDetailsVO;
    }

    @Override
    public Page<TicketRecordsVO> getTicketRecords(UserInfo user, Page page) {
        Page<TicketRecordsVO> ticketRecords = userSalvageEventsRecordsDao.getTicketRecords(page, user.getId());
        for (TicketRecordsVO record : ticketRecords.getRecords()) {
            record.setList(userSalvageEventsCardRecordsDao.getListByUserRecordId(record.getId()));
        }

        return ticketRecords;
    }

//--------------------------------------------------本类私有方法-------------------------------------------------------------------------------------------------------------

    /**
     * 根据用户藏品集合和分解活动信息,获取分解活动奖励
     *
     * @param userCollectionList
     * @param salvageEvents
     * @return
     */
    private List<SalvageEventsRewardsDTO> getSalvageEventsRewardsByUserCollectionListAndSalvageEventsInfo(List<UserCollection> userCollectionList, SalvageEventsInfo salvageEvents) {
        List<Long> userCollectionIdList = userCollectionList.stream().map(UserCollection::getId).collect(Collectors.toList());
        List<SalvageEventsRewardsDTO> salvageEventsRewardsDTOS = salvageEventsDetailsDao.getFixedPreSalvageReward(salvageEvents.getId(), userCollectionIdList);
        //region 神秘奖励
        List<Long> proportionPreSalvageReward = salvageEventsDetailsDao.getProportionPreSalvageReward(salvageEvents.getId(), userCollectionIdList);
        if (CollectionUtil.isNotEmpty(proportionPreSalvageReward)) {
            if (CollectionUtil.isEmpty(salvageEventsRewardsDTOS)) {
                salvageEventsRewardsDTOS = Lists.newArrayList();
            }
            SalvageEventsRewardsDTO salvageEventsRewardsDTO = new SalvageEventsRewardsDTO();
            salvageEventsRewardsDTO.setLinkCount(new BigDecimal(proportionPreSalvageReward.size()));
            salvageEventsRewardsDTO.setLinkType(AalvageEventsLinkEnum.MYSTERIOUS);
            salvageEventsRewardsDTO.setLinkName(AalvageEventsLinkEnum.MYSTERIOUS.getDescription());
            salvageEventsRewardsDTOS.add(salvageEventsRewardsDTO);
        }
        //endregion
        return salvageEventsRewardsDTOS;


//        List<SalvageEventsRewardsDTO> dtoList = Lists.newArrayList();
//把持有的藏品和数量统计出来 {"藏品id":x,"数量":x}
//        Map<Long, Integer> collectionCountMap = userCollectionList.stream().collect(Collectors.groupingBy(UserCollection::getCollectionId, Collectors.summingInt(i -> 1)));
//        for (Map.Entry<Long, Integer> entry : collectionCountMap.entrySet()) {
//            Long collectionId = entry.getKey();
//            int count = entry.getValue();
//            log.info("藏品id:{},选择数量:{}" ,collectionId, count);
//
//            //根据藏品id,获取分解活动的藏品奖励
//            List<SalvageEventsDetails> salvageEventsDetailsList = salvageEventsDetailsDao.getListByCollectionId(salvageEvents.getId(),collectionId);
//            for (SalvageEventsDetails salvageEventsDetails : salvageEventsDetailsList) {
//                SalvageEventsRewardsDTO salvageEventsRewardsDTO = new SalvageEventsRewardsDTO();
//                salvageEventsRewardsDTO.setLinkCount(salvageEventsDetails.getLinkCount().multiply(Convert.toBigDecimal(count)));
//                salvageEventsRewardsDTO.setLinkType(salvageEventsDetails.getLinkType());
//                salvageEventsRewardsDTO.setLinkName(salvageEventsDetails.getLinkName());
//                salvageEventsRewardsDTO.setLinkId(salvageEventsDetails.getLinkId());
//                dtoList.add(salvageEventsRewardsDTO);
//            }
//        }
//        return dtoList;
    }

    /**
     * 分解奖励结果聚合
     *
     * @param dtoList
     * @return
     */
    private List<GetSalvageEventsRewardsVO> decompositionRewardResultsAggregation(List<SalvageEventsRewardsDTO> dtoList) {
        List<GetSalvageEventsRewardsVO> voList = Lists.newArrayList();
        //原石集合
        List<SalvageEventsRewardsDTO> roughStoneList = dtoList.stream().filter(o -> o.getLinkType() == AalvageEventsLinkEnum.ROUGH_STONE).collect(Collectors.toList());
        if (roughStoneList.size() > 0) {//处理原石集合
            BigDecimal sumCount = roughStoneList.stream().map(SalvageEventsRewardsDTO::getLinkCount).reduce(BigDecimal.ZERO, BigDecimal::add);
            if (sumCount.compareTo(BigDecimal.ZERO) == 1) { //大于0
                GetSalvageEventsRewardsVO roughStone = new GetSalvageEventsRewardsVO();
                roughStone.setLinkName(roughStoneList.get(0).getLinkName());
                roughStone.setLinkCount(sumCount);
                roughStone.setLinkType(roughStoneList.get(0).getLinkType()); // 设置linkType属性
                roughStone.setLinkId(0L);
                roughStone.setLinkImg(roughStoneList.get(0).getLinkType().getImg());
                voList.add(roughStone);
            }
        }

        //积分集合
        List<SalvageEventsRewardsDTO> integralList = dtoList.stream().filter(o -> o.getLinkType() == AalvageEventsLinkEnum.INTEGRAL).collect(Collectors.toList());
        if (integralList.size() > 0) {//处理原石集合
            BigDecimal sumCount = integralList.stream().map(SalvageEventsRewardsDTO::getLinkCount).reduce(BigDecimal.ZERO, BigDecimal::add);
            if (sumCount.compareTo(BigDecimal.ZERO) == 1) { //大于0
                GetSalvageEventsRewardsVO integral = new GetSalvageEventsRewardsVO();
                integral.setLinkName(integralList.get(0).getLinkName());
                integral.setLinkCount(sumCount);
                integral.setLinkType(integralList.get(0).getLinkType());
                integral.setLinkId(0L);
                integral.setLinkImg(integralList.get(0).getLinkType().getImg());
                voList.add(integral);
            }
        }

        //道具集合
        //以linkId分组,然后把linkCount相加
        List<SalvageEventsRewardsDTO> propsList = dtoList.stream().filter(o -> o.getLinkType() == AalvageEventsLinkEnum.PROPS).collect(Collectors.toList());
        if (propsList.size() > 0) {
            List<GetSalvageEventsRewardsVO> groupedSumList = propsList.stream()
                    .collect(Collectors.groupingBy(SalvageEventsRewardsDTO::getLinkId))
                    .entrySet().stream()
                    .map(entry -> {
                        BigDecimal sum = entry.getValue()
                                .stream()
                                .map(SalvageEventsRewardsDTO::getLinkCount)
                                .reduce(BigDecimal.ZERO, BigDecimal::add);

                        GetSalvageEventsRewardsVO vo = new GetSalvageEventsRewardsVO();
                        vo.setLinkName(entry.getValue().get(0).getLinkName());
                        vo.setLinkId(entry.getKey());
                        vo.setLinkCount(sum);
                        vo.setLinkType(entry.getValue().get(0).getLinkType()); // 设置linkType属性
                        vo.setLinkImg(entry.getValue().get(0).getLinkType().getImg());
                        return vo;
                    })
                    .collect(Collectors.toList());


            voList.addAll(groupedSumList);
        }

        //燃料集合
        List<SalvageEventsRewardsDTO> fuelReleaseAllowanceList = dtoList.stream().filter(o -> o.getLinkType() == AalvageEventsLinkEnum.FUEL_RELEASE_ALLOWANCE).collect(Collectors.toList());
        if (fuelReleaseAllowanceList.size() > 0) {//处理原石集合
            BigDecimal sumCount = fuelReleaseAllowanceList.stream().map(SalvageEventsRewardsDTO::getLinkCount).reduce(BigDecimal.ZERO, BigDecimal::add);
            if (sumCount.compareTo(BigDecimal.ZERO) == 1) { //大于0
                GetSalvageEventsRewardsVO fuelReleaseAllowance = new GetSalvageEventsRewardsVO();
                fuelReleaseAllowance.setLinkName(fuelReleaseAllowanceList.get(0).getLinkName());
                fuelReleaseAllowance.setLinkCount(sumCount);
                fuelReleaseAllowance.setLinkType(fuelReleaseAllowanceList.get(0).getLinkType()); // 设置linkType属性
                fuelReleaseAllowance.setLinkId(0L);
                fuelReleaseAllowance.setLinkImg(fuelReleaseAllowanceList.get(0).getLinkType().getImg());
                voList.add(fuelReleaseAllowance);
            }
        }


        //铸造上限
        //以linkId分组,然后把linkCount相加
        List<SalvageEventsRewardsDTO> upperCastingLimitList = dtoList.stream().filter(o -> o.getLinkType() == AalvageEventsLinkEnum.UPPER_CASTING_LIMIT).collect(Collectors.toList());
        if (upperCastingLimitList.size() > 0) {
            List<GetSalvageEventsRewardsVO> groupedUpperCastingLimitList = upperCastingLimitList.stream()
                    .collect(Collectors.groupingBy(SalvageEventsRewardsDTO::getLinkId))
                    .entrySet().stream()
                    .map(entry -> {
                        BigDecimal sum = entry.getValue()
                                .stream()
                                .map(SalvageEventsRewardsDTO::getLinkCount)
                                .reduce(BigDecimal.ZERO, BigDecimal::add);

                        GetSalvageEventsRewardsVO vo = new GetSalvageEventsRewardsVO();
                        vo.setLinkName(entry.getValue().get(0).getLinkName());
                        vo.setLinkId(entry.getKey());
                        vo.setLinkCount(sum);
                        vo.setLinkType(entry.getValue().get(0).getLinkType()); // 设置linkType属性
                        vo.setLinkImg(entry.getValue().get(0).getLinkType().getImg());
                        return vo;
                    })
                    .collect(Collectors.toList());
            voList.addAll(groupedUpperCastingLimitList);
        }

        //重铸上限
        List<SalvageEventsRewardsDTO> upperReforgeCastingLimit = dtoList.stream().filter(o -> o.getLinkType() == AalvageEventsLinkEnum.UPPER_REFORGE_CASTING_LIMIT).collect(Collectors.toList());
        if (upperReforgeCastingLimit.size() > 0) {
            List<GetSalvageEventsRewardsVO> groupedUpperReforgeCastingLimit = upperReforgeCastingLimit.stream()
                    .collect(Collectors.groupingBy(SalvageEventsRewardsDTO::getLinkId))
                    .entrySet().stream()
                    .map(entry -> {
                        BigDecimal sum = entry.getValue()
                                .stream()
                                .map(SalvageEventsRewardsDTO::getLinkCount)
                                .reduce(BigDecimal.ZERO, BigDecimal::add);

                        GetSalvageEventsRewardsVO vo = new GetSalvageEventsRewardsVO();
                        vo.setLinkName(entry.getValue().get(0).getLinkName());
                        vo.setLinkId(entry.getKey());
                        vo.setLinkCount(sum);
                        vo.setLinkType(entry.getValue().get(0).getLinkType()); // 设置linkType属性
                        vo.setLinkImg(entry.getValue().get(0).getLinkType().getImg());
                        return vo;
                    })
                    .collect(Collectors.toList());
            voList.addAll(groupedUpperReforgeCastingLimit);
        }

        //神秘奖励
        Optional<SalvageEventsRewardsDTO> optional = dtoList.stream().filter(o -> o.getLinkType() == AalvageEventsLinkEnum.MYSTERIOUS).findFirst();
        if (optional.isPresent()) {
            SalvageEventsRewardsDTO salvageEventsRewardsDTO = optional.get();
            GetSalvageEventsRewardsVO vo = new GetSalvageEventsRewardsVO();
            vo.setLinkName(salvageEventsRewardsDTO.getLinkName());
            vo.setLinkCount(salvageEventsRewardsDTO.getLinkCount());
            vo.setLinkType(salvageEventsRewardsDTO.getLinkType()); // 设置linkType属性
            vo.setLinkImg(salvageEventsRewardsDTO.getLinkType().getImg());
            voList.add(vo);
        }

        return voList;
    }
}